package com.one.square.oauth.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONObject;
import com.one.square.common.redis.util.RedisUtils;
import com.one.square.core.constant.SecurityConstants;
import com.one.square.core.utils.JwtUtils;
import com.one.square.oauth.entity.Token;
import com.one.square.oauth.service.TokenService;
import lombok.AllArgsConstructor;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.util.*;

/**
 * @author cwm
 * @Description
 * @date 2021/10/28 下午1:28
 * @Version 1.0
 */
@Service
@AllArgsConstructor
public class RedisTokensServiceImpl implements TokenService {
    private final RedisTokenStore redisTokenStore;
    private final JdbcClientDetailsService clientDetailsService;
    private final RedisUtils redisUtils;
    @Override
    public List<Token> list() {
        //获取所有客户端id
        List<ClientDetails> clientDetails = clientDetailsService.listClientDetails();
        List<Token> result =new ArrayList<>();
        clientDetails.stream().forEach(item ->{
            Collection<OAuth2AccessToken> tokensByClientId = redisTokenStore.findTokensByClientId(item.getClientId());

            Iterator<OAuth2AccessToken> iterator = tokensByClientId.iterator();
            if (iterator.hasNext()){
                OAuth2AccessToken oAuth2AccessToken = iterator.next();
                JSONObject jwtPayload = null;
                try {
                    jwtPayload= JwtUtils.getJwtPayload(oAuth2AccessToken.getValue());
                    String jti = jwtPayload.getStr(SecurityConstants.JWT_JTI);
                    boolean b = redisUtils.hasKey(SecurityConstants.TOKEN_BLACKLIST_PREFIX + jti);
                    //排除黑名单token
                    if (!b){
                        Token token = new Token();
                        token.setTokenValue(oAuth2AccessToken.getValue());
                        token.setExpiration(oAuth2AccessToken.getExpiration());
                        token.setClientId(item.getClientId());
                        Map<String, Object> additionalInformation = oAuth2AccessToken.getAdditionalInformation();
                        if (CollectionUtil.isNotEmpty(additionalInformation)){
                            Object o = additionalInformation.get(SecurityConstants.GRANT_TYPE_KEY);
                            if (Objects.nonNull(o)){
                                token.setGrantType(o.toString());
                            }
                            Object o1 = additionalInformation.get(SecurityConstants.USER_NAME_KEY);
                            if (Objects.nonNull(o1)){
                                token.setUsername(o1.toString());
                            }
                        }
                        result.add(token);
                    }
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
        });
        return result;
    }

    @Override
    public void delete(String tokenValue) throws ParseException {
        //移除数redis的token信息
        redisTokenStore.removeAccessToken(tokenValue);
        JSONObject payload = JwtUtils.getJwtPayload(tokenValue);
        // JWT唯一标识
        String jti = payload.getStr(SecurityConstants.JWT_JTI);
        // JWT过期时间戳(单位：秒)
        Long expireTime = payload.getLong(SecurityConstants.JWT_EXP);
        if (expireTime != null) {
            // 当前时间（单位：秒）
            long currentTime = System.currentTimeMillis() / 1000;
            if (expireTime > currentTime) {
                // token未过期，添加至缓存作为黑名单限制访问，缓存时间为token过期剩余时间
                redisUtils.set(SecurityConstants.TOKEN_BLACKLIST_PREFIX + jti, null, (expireTime - currentTime));
            }
        } else {
            // token 永不过期则永久加入黑名单
            redisUtils.set(SecurityConstants.TOKEN_BLACKLIST_PREFIX + jti, null);
        }
    }
}
